﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace LambdaTreeDemo
{
    public class Program
    {
        static void Main(string[] args)
        {
            //NewObject();
            //GetProp();
            //AssignProp();
            //CallGetHashCode();
            //CallSayHello();
            //CallLinqContains();
            //CallLinqContainsAndAny();
            //TernaryOperation();
            //NullExp();
            //ConvertInt();
            //NewArrayBounds();
            //ConditionExp();
            ComplexDemo();
        }

        // a => a + 1
        private static void AddMethod()
        {
            ParameterExpression pe = Expression.Parameter(typeof(int), "a");
            ConstantExpression ce = Expression.Constant(1);
            BinaryExpression binaryExpression = Expression.Add(pe, ce);

            Expression<Func<int, int>> lambdaExpression = Expression.Lambda<Func<int, int>>(binaryExpression, pe);
            Func<int, int> func = lambdaExpression.Compile();

            Console.WriteLine("binaryExpression={0}", binaryExpression);//(a + 1)
            Console.WriteLine("lambdaExpression={0}", lambdaExpression);// a => (a + 1)
            Console.WriteLine("func={0}", func);
            Console.WriteLine("result={0}", func(3));
            Console.ReadKey();
        }

        // (a,b)=>a*3+b*4
        private static void MultiplyAndAddMethod()
        {
            ParameterExpression pa = Expression.Parameter(typeof(int), "a");
            ConstantExpression ca = Expression.Constant(3);
            BinaryExpression ae = Expression.Multiply(pa, ca);
            Console.WriteLine("ae={0}", ae);

            ParameterExpression pb = Expression.Parameter(typeof(int), "b");
            ConstantExpression cb = Expression.Constant(4);
            BinaryExpression be = Expression.Multiply(pb, cb);
            Console.WriteLine("be={0}", be);

            BinaryExpression fe = Expression.Add(ae, be); // a*3+b*4
            Console.WriteLine("fe={0}", fe);

            Expression<Func<int, int, int>> lambdaExpression = Expression.Lambda<Func<int, int, int>>(fe, pa, pb);
            Console.WriteLine("lambdaExpression={0}", lambdaExpression);
            Func<int, int, int> func = lambdaExpression.Compile();
            Console.WriteLine("func={0}", func);
            Console.WriteLine("result={0}", func(2, 3));

            Console.ReadKey();
        }

        //(a,b,c,d,e)=>((a+b)*(c-d))%e
        private static void CompositeMethod()
        {
            ParameterExpression pa = Expression.Parameter(typeof(int), "a");
            ParameterExpression pb = Expression.Parameter(typeof(int), "b");
            BinaryExpression abe = Expression.Add(pa, pb); // (a+b)
            Console.WriteLine("abe={0}", abe);

            ParameterExpression pc = Expression.Parameter(typeof(int), "c");
            ParameterExpression pd = Expression.Parameter(typeof(int), "d");
            BinaryExpression cde = Expression.Subtract(pc, pd); // (c-d)
            Console.WriteLine("cde={0}", cde);

            BinaryExpression me = Expression.Multiply(abe, cde); // (a+b) * (c-d)
            Console.WriteLine("me={0}", me);
            ParameterExpression pe = Expression.Parameter(typeof(int), "e");
            BinaryExpression fe = Expression.Modulo(me, pe);
            Console.WriteLine("fe={0}", fe);//((a+b)*(c-d))%e

            Expression<Func<int, int, int, int, int, int>> expression = Expression.Lambda<Func<int, int, int, int, int, int>>(fe, pa, pb, pc, pd, pe);
            Console.WriteLine("expression={0}", expression); // (a, b, c, d, e) => ((a + b) * (c - d)) % e
            Func<int, int, int, int, int, int> func = expression.Compile();
            Console.WriteLine("result={0}", func(1, 2, 4, 3, 5));

            Console.ReadKey();
        }

        // 创建对象
        private static void NewObject()
        {
            NewExpression newExpression = Expression.New(typeof(MyClass).GetConstructor(new[] { typeof(int) }), Expression.Constant(100)); // 有参构造函数方式
            //var newExpression = Expression.New(typeof(MyClass)); //无参构造函数方式
            Console.WriteLine("newExpression={0}", newExpression);// new MyClass(100)

            Expression<Func<MyClass>> lambda = Expression.Lambda<Func<MyClass>>(newExpression);
            Console.WriteLine("lambda={0}", lambda);// () => new MyClass(100)
            Func<MyClass> myClass = lambda.Compile();
            MyClass myObj = myClass();
            Console.WriteLine("myObj.Number={0}", myObj.Number);

            Console.ReadKey();
        }

        // 获取属性值e=>e.MyProperty
        private static void GetProp()
        {
            MyClass ob = new MyClass(100);
            ob.MyProperty = "abc";

            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");
            Console.WriteLine("e={0}", e);// e

            MemberExpression property = Expression.Property(e, "MyProperty");
            Console.WriteLine("property={0}", property);// e.MyProperty 

            Expression<Func<MyClass, string>> lambda = Expression.Lambda<Func<MyClass, string>>(property, e);
            Console.WriteLine("lambda={0}", lambda); // e => e.MyProperty 

            Func<MyClass, string> func = lambda.Compile();
            Console.WriteLine("result={0}", func(ob));

            Console.ReadKey();
        }

        // 为属性赋值e=>e.List=new List<string>(){"sdf"}
        private static void AssignProp()
        {
            NewExpression newExpression = Expression.New(typeof(MyClass).GetConstructor(new[] { typeof(int) }), Expression.Constant(100)); // 有参构造函数方式
            Expression<Func<MyClass>> lambda = Expression.Lambda<Func<MyClass>>(newExpression);
            MyClass myClass = lambda.Compile()();

            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");

            MemberExpression property = Expression.Property(e, nameof(MyClass.MyProperty));
            BinaryExpression assignString = Expression.Assign(property, Expression.Constant("sss"));//为字符串类型的属性赋值
            Console.WriteLine("assignString={0}", assignString);

            BinaryExpression assignList = Expression.Assign(Expression.Property(e, "List"), Expression.Constant(new List<string>() { "sdf" }));//为集合类型的属性赋值
            Console.WriteLine("assignList={0}", assignList);

            Expression.Lambda<Action<MyClass>>(assignList, e).Compile()(myClass);
            Expression.Lambda<Action<MyClass>>(assignString, e).Compile()(myClass);// 因为赋值操作没有返回值，所以是Action，并且只有一个入参

            Console.WriteLine("MyProperty={0},List={1}", myClass.MyProperty, myClass.List[0]);
            Console.ReadKey();
        }

        // 调用方法e=>e.GetHashCode();
        private static void CallGetHashCode()
        {
            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");
            MethodInfo method = typeof(MyClass).GetMethod(nameof(GetHashCode));//获取MyClass的GetHashCode方法
            MethodCallExpression call = Expression.Call(e, method);//e.GetHashCode()
            Expression<Func<MyClass, int>> lambda = Expression.Lambda<Func<MyClass, int>>(call, e);

            Console.WriteLine("result={0}", lambda.Compile()(new MyClass(23)));
            Console.ReadKey();
        }

        // 调用有参方法e=>e.SayHello("你好")
        private static void CallSayHello()
        {
            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");
            MethodInfo method = typeof(MyClass).GetMethod(nameof(MyClass.SayHello), new[] { typeof(string) });
            MethodCallExpression call = Expression.Call(e, method, Expression.Constant("你好"));//调用SayHello方法并给方法传入"你好"参数
            Console.WriteLine("call={0}", call);//e.SayHello("你好")

            Expression<Func<MyClass, string>> lambda = Expression.Lambda<Func<MyClass, string>>(call, e);
            Console.WriteLine("lambda={0}", lambda);//e => e.SayHello("你好")

            Console.WriteLine("result={0}", lambda.Compile()(new MyClass(23)));
            Console.ReadKey();
        }

        // 调用Linq扩展方法e=>e.List.Contains("s")
        private static void CallLinqContains()
        {
            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");
            MemberExpression list = Expression.Property(e, nameof(MyClass.List));
            Console.WriteLine("list={0}", list);//e.List

            MethodInfo containsMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Contains").MakeGenericMethod(typeof(string));
            MethodCallExpression contains = Expression.Call(null, containsMethod, list, Expression.Constant("s"));
            Console.WriteLine("contains={0}", contains);//e.List.Contains("s")
            Expression<Func<MyClass, bool>> lambda = Expression.Lambda<Func<MyClass, bool>>(contains, e);
            Console.WriteLine("lambda={0}", lambda);//e => e.List.Contains("s")

            Func<MyClass, bool> func = lambda.Compile();
            Console.WriteLine("result={0}", func(new MyClass(12) { List = new List<string>() { "sa", "a" } }));
            Console.ReadKey();
        }

        // 调用Linq扩展方法 e=>e.List.Any(item=>item.Contains("s"))
        private static void CallLinqContainsAndAny()
        {
            ParameterExpression pe = Expression.Parameter(typeof(MyClass), "e");

            ParameterExpression pitem = Expression.Parameter(typeof(string), "item");
            MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            MethodCallExpression containsExp = Expression.Call(pitem, containsMethod, Expression.Constant("s"));
            Console.WriteLine("containsExp={0}", containsExp);
            LambdaExpression containsLambda = Expression.Lambda(containsExp, pitem);//item=>item.Contains("s")
            Console.WriteLine("containsLambda={0}", containsLambda);

            MethodInfo anyMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Any").MakeGenericMethod(typeof(string));
            MemberExpression list = Expression.Property(pe, nameof(MyClass.List));//e.List
            Console.WriteLine("list={0}", list);
            MethodCallExpression anyCallExp = Expression.Call(null, anyMethod, list, containsLambda);
            Console.WriteLine("anyCallExp={0}", anyCallExp);

            Expression<Func<MyClass, bool>> lambda = Expression.Lambda<Func<MyClass, bool>>(anyCallExp, pe);
            Console.WriteLine("lambda={0}", lambda);
            Func<MyClass, bool> func = lambda.Compile();
            Console.WriteLine("result={0}", func(new MyClass(12) { List = new List<string>() { "sb", "a" } }));

            Console.ReadKey();
        }

        // 三目表达式x=>x>60?"及格":"不及格"
        private static void TernaryOperation()
        {
            ParameterExpression px = Expression.Parameter(typeof(int), "x");
            BinaryExpression gt60 = Expression.GreaterThan(px, Expression.Constant(60));//x>60
            Console.WriteLine("gt60={0}", gt60);

            ConditionalExpression condition = Expression.Condition(gt60, Expression.Constant("及格"), Expression.Constant("不及格"));//x>60?"及格":"不及格"
            Console.WriteLine("condition={0}", condition);
            Expression<Func<int, string>> lambda = Expression.Lambda<Func<int, string>>(condition, px);//x=>x>60?"及格":"不及格"
            Console.WriteLine("lambda={0}", lambda);

            Console.WriteLine("result={0}", lambda.Compile()(10));

            Console.ReadKey();
        }

        // null值表达式e => e.MyProperty ?? "s"
        private static void NullExp()
        {
            ParameterExpression e = Expression.Parameter(typeof(MyClass), "e");
            MemberExpression expression = Expression.Property(e, "MyProperty");
            BinaryExpression coalesce = Expression.Coalesce(expression, Expression.Constant("s"));
            Console.WriteLine("coalesce={0}", coalesce);
            Expression<Func<MyClass, string>> lambda = Expression.Lambda<Func<MyClass, string>>(coalesce, e);
            Console.WriteLine("lambda={0}", lambda);
            Func<MyClass, string> func = lambda.Compile();
            Console.WriteLine("result={0}", func(new MyClass(12) { MyProperty = "aaa" }));

            Console.ReadKey();
        }

        // 类型转换
        private static void ConvertInt()
        {
            ParameterExpression pe = Expression.Parameter(typeof(float), "x");
            UnaryExpression convert = Expression.Convert(pe, typeof(int));
            Console.WriteLine("convert={0}", convert);
            Expression<Func<float, int>> lambda = Expression.Lambda<Func<float, int>>(convert, pe);
            Console.WriteLine("lambda={0}", lambda);

            Console.WriteLine("result={0}", lambda.Compile()(10.23f));
            Console.ReadKey();
        }

        // 声明一个数组对象
        private static void NewArrayBounds()
        {
            NewArrayExpression array = Expression.NewArrayBounds(typeof(string), Expression.Constant(5));
            Console.WriteLine("array={0}", array);
            Expression<Func<string[]>> lambda = Expression.Lambda<Func<string[]>>(array);
            Console.WriteLine("lambda={0}", lambda);
            string[] b = lambda.Compile()();
            Console.WriteLine("result={0}-{1}", b.GetType(), b.Length);
            Console.ReadKey();
        }

        // 实现条件表达式m=>m.MyProperty.Contains("ldqk")||m.List.Any(s=>s.Length>1&&s.Contains("a"))
        private static void ConditionExp()
        {
            ParameterExpression s = Expression.Parameter(typeof(string), "s");
            MemberExpression length = Expression.Property(s, "Length"); // s.Length
            BinaryExpression gt10 = Expression.GreaterThan(length, Expression.Constant(1));// s.Length>1

            MethodCallExpression anyContains = Expression.Call(s, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant("a"));// s.Contains("a")

            BinaryExpression anyWhere = Expression.AndAlso(gt10, anyContains); // s.Length>1&&s.Contains("a")
            LambdaExpression anyLambda = Expression.Lambda(anyWhere, s);// s=>s.Length>1&&s.Contains("a")

            MethodInfo any = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Any").MakeGenericMethod(typeof(string));
            ParameterExpression m = Expression.Parameter(typeof(MyClass), "m");
            MemberExpression prop = Expression.Property(m, "List");// List.Any(s=>s.Length>1&&s.Contains("a")
            MethodCallExpression whereLambda = Expression.Call(null, any, prop, anyLambda); // m.List.Any(s=>s.Length>1&&s.Contains("a")

            MemberExpression property = Expression.Property(m, nameof(MyClass.MyProperty));// m.MyProperty
            MethodCallExpression contains = Expression.Call(property, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant("ldqk"));// m.MyProperty.Contains("ldqk")
            BinaryExpression orExp = Expression.OrElse(contains, whereLambda);// m.MyProperty.Contains("ldqk")||m.List.Any(s=>s.Length>1&&s.Contains("a"))
            Expression<Func<MyClass, bool>> lambda = Expression.Lambda<Func<MyClass, bool>>(orExp, m); // m=>m.MyProperty.Contains("ldqk")||m.List.Any(s=>s.Length>1&&s.Contains("a"))
        }

        // 链式调用s=>s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() > 1
        public static void ComplexDemo()
        {
            ParameterExpression s = Expression.Parameter(typeof(MyClass), "s");
            MemberExpression prop = Expression.Property(s, "List"); // s.List
            MethodInfo selectMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Select").MakeGenericMethod(typeof(string), typeof(int));
            ParameterExpression e = Expression.Parameter(typeof(string), "e");
            MemberExpression selectlength = Expression.Property(e, "Length"); // e.Length
            LambdaExpression selectLambda = Expression.Lambda(selectlength, e); // e => e.Length
            MethodCallExpression select = Expression.Call(null, selectMethod, prop, selectLambda); //s.List.Select(e => e.Length)
            MethodInfo orderby = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "OrderBy").MakeGenericMethod(typeof(int), typeof(int));
            ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
            LambdaExpression orderbyLambda = Expression.Lambda(parameter, parameter); //x=>x
            MethodCallExpression orderbyCall = Expression.Call(null, @orderby, @select, orderbyLambda); //s.List.Select(e => e.Length).OrderBy(x=>x)
            MethodInfo first = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 1 && info.Name == "FirstOrDefault").MakeGenericMethod(typeof(int));
            MethodCallExpression firstExp = Expression.Call(null, first, orderbyCall); //s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault()
            BinaryExpression greaterThan = Expression.GreaterThan(firstExp, Expression.Constant(1)); //s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() > 1
            Expression<Func<MyClass, bool>> lambda = Expression.Lambda<Func<MyClass, bool>>(greaterThan, s); //s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() >= 1
            Func<MyClass, bool> func = lambda.Compile();
            Console.WriteLine("result={0}", func(new MyClass(12) { List = new List<string>() { "sq", "ab" } }));

            Console.ReadKey();
        }
    }
}
